﻿namespace MicroCloud.Entity
{
    /// <summary>
    /// 数据迁移模块基类
    /// </summary>
    /// <typeparam name="TDbContext">数据上下文类型</typeparam>
    [Description("数据迁移模块")]
    public abstract class MigrationPackBase<TDbContext> : PackBase
        where TDbContext : DbContext, IDbContext
    {
        //字段
        private MicroCloudOptions _options;

        #region 属性
        /// <summary>
        /// 获取 模块级别
        /// <para>级别越小越先启动</para>
        /// </summary>
        public override PackLevel Level => PackLevel.Application;
        
        /// <summary>
        /// 获取 模块启动顺序编号
        /// <para>模块启动的顺序先按级别启动，级别内部再按此顺序启动，级别默认为0，表示无依赖，需要在同级别有依赖顺序的时候，再重写为 >0 的顺序值。</para>
        /// </summary>
        public override int OrderNo => 0;

        /// <summary>
        /// 获取 数据库类型
        /// </summary>
        protected abstract DatabaseType DatabaseType { get; }

        #endregion

        #region "方法"
        /// <summary>
        /// 将模块服务添加到依赖注入服务容器中
        /// </summary>
        /// <param name="services">依赖注入服务容器</param>
        /// <returns></returns>
        public override IServiceCollection AddServices(IServiceCollection services)
        {
            _options = services.GetMicroCloudOptions();
            var dbContextOptions = _options?.GetDbContextOptions(typeof(TDbContext));
            if (dbContextOptions?.DatabaseType != DatabaseType)
            {
                return services;
            }
            services.AddMicroCloudDbContext<TDbContext>();

            services.AddSingleton<IEntityBatchConfiguration, PropertyEntityConfiguration>();
            services.AddSingleton<IEntityBatchConfiguration, PropertyCommentConfiguration>();
            //启用时间属性UTC格式
            if (dbContextOptions?.DateTimeUtcFormatEnabled == true)
            {
                services.AddSingleton<IEntityBatchConfiguration, PropertyUtcDateTimeConfiguration>();
            }
            services.AddSingleton<IEntityBatchConfiguration, PropertyKeyConfiguration>();
            services.AddSingleton<IEntityBatchConfiguration, PropertyPublicConfiguration>();
            services.AddSingleton<IEntityBatchConfiguration, PropertyConcurrencyTokenConfiguration>();

            return services;
        }

        /// <summary>
        /// 应用模块服务
        /// </summary>
        /// <param name="provider">服务提供者</param>
        public override void UsePack(IServiceProvider provider)
        {
            var dbContextOptions = _options?.GetDbContextOptions(typeof(TDbContext));
            if (dbContextOptions?.DatabaseType != DatabaseType)
            {
                return;
            }

            ILogger logger = provider.GetLogger(GetType());
            using (IServiceScope scope = provider.CreateScope())
            {
                TDbContext context = CreateDbContext(scope.ServiceProvider);
                if (context != null && dbContextOptions.MigrationEnabled)
                {
                    context.CheckAndMigration(logger);
                    DbContextModelCache modelCache = scope.ServiceProvider.GetService<DbContextModelCache>();
                    modelCache?.Set(context.GetType(), context.Model);
                }
            }

            //todo:多上下文同步同一个存储过程等: Hashkey 比较有缺陷
            
            //初始化种子数据，只初始化当前上下文的种子数据
            var entityManager = provider.GetRequiredService<IEntityManager>();
            var entityTypes = entityManager.GetEntityRegisters(typeof(TDbContext)).Select(m => m.EntityType).Distinct().ToArray();
            var seedDataInitializers = provider.GetServices<ISeedDataInitializer>().Where(m => entityTypes.Contains(m.EntityType)).OrderBy(m => m.OrderNo).ToList();
            foreach (ISeedDataInitializer initializer in seedDataInitializers)
            {
                initializer.Initialize();
            }
            //初始化数据库函数
            var functionHandlers = provider.GetServices<MicroCloud.Database.IFunctionHandler>().Where(m => m.DbContextOptions.Any(o => o.Key == typeof(TDbContext))).OrderBy(m => m.OrderNo).ToList();
            foreach (var functionHandler in functionHandlers)
            {
                functionHandler.Initialize(typeof(TDbContext));
            }
            //初始化数据库存储过程
            var procedureHandlers = provider.GetServices<IProcedureHandler>().Where(m => m.DbContextOptions.Any(o => o.Key == typeof(TDbContext))).OrderBy(m => m.OrderNo).ToList();
            foreach (var procedureHandler in procedureHandlers)
            {
                procedureHandler.Initialize(typeof(TDbContext));
            }
            //初始化数据库触发器
            var triggerHandlers = provider.GetServices<ITriggerHandler>().Where(m => m.DbContextOptions.Any(o => o.Key == typeof(TDbContext))).OrderBy(m => m.OrderNo).ToList();
            foreach (var triggerHandler in triggerHandlers)
            {
                triggerHandler.Initialize(typeof(TDbContext));
            }
            //初始化数据库视图
            var viewHandlers = provider.GetServices<IViewHandler>().Where(m => m.DbContextOptions.Any(o => o.Key == typeof(TDbContext))).OrderBy(m => m.OrderNo).ToList();
            foreach (var viewHandler in viewHandlers)
            {
                viewHandler.Initialize(typeof(TDbContext));
            }

            IsEnabled = true;
        }

        #endregion

        #region 抽象方法
        /// <summary>
        /// 重写实现获取数据上下文实例
        /// </summary>
        /// <param name="scopedProvider">服务提供者</param>
        /// <returns></returns>
        protected abstract TDbContext CreateDbContext(IServiceProvider scopedProvider);

        #endregion

    }
}
